package streamforce.crypto.cloud.subscriber;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import streamforce.crypto.KPAbe;
import streamforce.crypto.cloud.EPCloud;
import streamforce.crypto.cloud.EPCloudServer;
import streamforce.crypto.cloud.TimeTuple;
import streamforce.crypto.cloud.Tuple;
import streamforce.crypto.event.Ciphertext;
import streamforce.crypto.event.TransformedCTEventAllAgg3;
import streamforce.crypto.policies.Policy;

//Aggregate 3 implementation at the cloud
public class CloudSumAllAgg3 extends CloudSumPriceAgg2{
	final Log log = LogFactory.getLog(CloudSumAllAgg3.class);
	byte[] roPrice, rtPrice, roVolume, rtVolume; 
	byte[][] poPrice, poVolume, ptPrice, ptVolume; 
	byte[] prodOPrice, prodTPrice, prodOVolume, prodTVolume; 
	int windowSize;
	int rId; 
	long totalTime=0; 
	public CloudSumAllAgg3(int pi, int rpi, int windowSize, KPAbe ch){
		super(pi,ch);
		this.rId = rpi; 
		this.windowSize = windowSize;
	}		
	
	private synchronized void handlePrice(int timestamp, int hour, int stockId, byte[] ciphertext, int [] lens){			
		byte[] c0 = new byte[lens[0]];
		System.arraycopy(ciphertext,0,c0,0,lens[0]);
		byte[] e0 = this.cryptoHandler.extractOriginalCipher(stockId, c0);
		byte[] t0 = this.cryptoHandler.transform_simple(c0, stockId, this.policyId);			
		if (timestamp%this.windowSize==0){
			poPrice = new byte[windowSize][e0.length];
			ptPrice = new byte[windowSize][t0.length];				
		}
		poPrice[timestamp%this.windowSize] = e0; 
		ptPrice[timestamp%this.windowSize] = t0;
	}
	
	private void handleVolume(int timestamp, int hour, int stockId, byte[] ciphertext, int [] lens){			
		byte[] c0 = new byte[lens[0]];
		System.arraycopy(ciphertext,0,c0,0,lens[0]);
		byte[] e0 = this.cryptoHandler.extractOriginalCipher(stockId, c0);
		byte[] t0 = this.cryptoHandler.transform_simple(c0, stockId, this.policyId);			
		if (timestamp%this.windowSize==0){
			poVolume = new byte[windowSize][e0.length];
			ptVolume = new byte[windowSize][t0.length];				
		}
		poVolume[timestamp%this.windowSize] = e0; 
		ptVolume[timestamp%this.windowSize] = t0;
	}
	
	//the ciphertext here now contains 2 smaller ciphers
	public void update(int timestamp, int hour, int stockId, Ciphertext ctPrice, Ciphertext ctVolume) {				
		if (ctPrice != null && ctVolume !=null) {			
			TimeTuple tt = EPCloudServer.tupleTime.get(new Tuple(stockId, timestamp)); 
			if (tt.getCount()==0)
				this.statTuple.update(); 			
			tt.increaseCount();
			
			byte[] ciphertextPrice = ctPrice.getCipher();
			byte[] ciphertextVolume = ctVolume.getCipher();
			
			long start = System.currentTimeMillis(); 
			
			int[] lensPrice = ctPrice.getLens();		
			this.handlePrice(timestamp, hour, stockId, ciphertextPrice, lensPrice);
			
			int[] lensVolume = ctVolume.getLens();
			this.handleVolume(timestamp, hour, stockId, ciphertextVolume, lensVolume);						
			
			long end = System.currentTimeMillis(); 
			this.totalTime+=(end-start); 
			
			if (timestamp%this.windowSize == (this.windowSize-1)){
				start = System.currentTimeMillis(); 
				prodOPrice = this.cryptoHandler.multiplyCiphers(poPrice, stockId);
				prodTPrice = this.cryptoHandler.multiplyCiphers(ptPrice, stockId);
				prodOVolume = this.cryptoHandler.multiplyCiphers(poVolume, stockId);
				prodTVolume = this.cryptoHandler.multiplyCiphers(ptVolume, stockId);
				
				byte[] c1Price = new byte[lensPrice[1]];				
				System.arraycopy(ciphertextPrice,lensPrice[0],c1Price,0,lensPrice[1]);
				rtPrice = this.cryptoHandler.transform_simple(c1Price, stockId, this.rId); 
				roPrice = this.cryptoHandler.extractOriginalCipher(stockId, c1Price);
				
				byte[] c1Volume = new byte[lensVolume[1]];				
				System.arraycopy(ciphertextVolume,lensVolume[0],c1Volume,0,lensVolume[1]);
				rtVolume = this.cryptoHandler.transform_simple(c1Volume, stockId, this.rId); 
				roVolume = this.cryptoHandler.extractOriginalCipher(stockId, c1Volume);
					
				/*int val = this.cryptoHandler.decrypt_slidingWindow_Aggregate3(
						this.policyId, this.rId, prodT, prodO, rt, ro);
				log.info("DECRYPTED = "+val);*/
				byte[][] aggOriginals = new byte[2][prodOPrice.length];
				byte[][] aggTransforms = new byte[2][prodTPrice.length];
				byte[][] aggPrevOriginals = new byte[2][roPrice.length];
				byte[][] aggPrevTransforms = new byte[2][rtPrice.length];
				aggOriginals[0] = prodOPrice; aggOriginals[1] =  prodOVolume; 
				aggTransforms[0] = prodTPrice; aggTransforms[1] =  prodTVolume;
				aggPrevOriginals[0] = roPrice; aggPrevOriginals[1] =  roVolume; 
				aggPrevTransforms[0] = rtPrice; aggPrevTransforms[1] =  rtVolume;				
				TransformedCTEventAllAgg3 cte = new TransformedCTEventAllAgg3(
						this.policyId, this.rId, timestamp, hour, stockId, 
						aggOriginals, aggTransforms, aggPrevOriginals,
						aggPrevTransforms);				
				
				end = System.currentTimeMillis();
				this.totalTime +=(end-start); 
				log.info("agg3 transform time = " + this.totalTime);
				this.totalTime = 0; 
				
				EPCloud.getEPServiceProvider().getEPRuntime().sendEvent(cte);
				
			}																	
		}
		else
			log.error("WHAT? NULL HERE!!");
	}
}
